﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Configuration;

namespace CYTS.Aoyou.Framework.Utility
{
    public class FileMonitorHelper
    {
        private static readonly object _PathLock = new object();
        private static readonly object _DicLock = new object();

        /// <summary>
        /// 更新间隔 单位ms 当前值 5分钟
        /// </summary>
        private const int UpdateInterval = 1000 * 60 * 5;
        private static Random ran = new Random(DateTime.Now.Millisecond);

        private const int MIN_RETRY_MS = 1000 * 60 * 1;
        private const int MAX_RETRY_MS = 1000 * 60 * 2;

        private static readonly string _ErrorEmailTo = string.IsNullOrEmpty(System.Configuration.ConfigurationManager.AppSettings["ConfigErrorEmailTo"]) ? "shaz@aoyou.com" : System.Configuration.ConfigurationManager.AppSettings["ConfigErrorEmailTo"];
        private static readonly string _ErrorEmailFrom = string.IsNullOrEmpty(System.Configuration.ConfigurationManager.AppSettings["ConfigErrorEmailFrom"]) ? "info@aoyou.com" : System.Configuration.ConfigurationManager.AppSettings["ConfigErrorEmailFrom"];
        private const string _ErrorEmailSubject = "配置文件模块报错";
        private static string _HostName = System.Net.Dns.GetHostName();

        public static event Action<FileInfo> OnFileModified;
        public static event Action OnFileModifyCompleted;

        /// <summary>
        /// 配置文件路径
        /// </summary>
        private static string _Path;

        /// <summary>
        /// 配置文件路径
        /// </summary>
        public static string Path
        {
            get
            {
                if (string.IsNullOrEmpty(_Path))
                {
                    lock (_PathLock)
                    {
                        if (string.IsNullOrEmpty(_Path))
                        {
                            var path = ConfigurationManager.AppSettings["MonitorFilesPath"];
                            _Path = System.IO.Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory + "\\" + path);
                        }
                    }
                }
                return _Path;
            }
            set
            {
                _Path = value;
            }
        }

        private static Dictionary<string, string> _MonitorFileFullPathDic;

        /// <summary>
        /// 配置文件全路径Dictionary《文件名，全路径》
        /// </summary>
        public static Dictionary<string, string> MonitorFileFullPathDic
        {
            get
            {
                if (_MonitorFileFullPathDic == null)
                {
                    try
                    {
                        Monitor.Enter(_DicLock);
                        if (_MonitorFileFullPathDic == null)
                        {
                            _MonitorFileFullPathDic = new Dictionary<string, string>();
                            var allFiles = Directory.GetFiles(Path, "*.*", SearchOption.AllDirectories);
                            foreach (string item in allFiles)
                            {
                                string configFileName = System.IO.Path.GetFileName(item).ToLower();
                                string configFileFullPath = System.IO.Path.GetFullPath(item).ToLower();
                                if (_MonitorFileFullPathDic.ContainsKey(configFileName))
                                {
                                    string msg = string.Format("错误：配置文件根目录下存在多个名为'{0}'的配置文件！", configFileName);
                                    throw new Exception(msg);
                                }
                                _MonitorFileFullPathDic.Add(configFileName, configFileFullPath);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        string msg = "配置文件全路径Dictionary《文件名，全路径》失败";
                        //EmailHelper.SendMail("", _ErrorEmailTo, _ErrorEmailSubject, false, msg + "\n" + ex, "", "");
                        throw new Exception(msg, ex);
                    }
                    finally
                    {
                        Monitor.Exit(_DicLock);
                    }
                }
                return _MonitorFileFullPathDic;
            }
            set
            {
                _MonitorFileFullPathDic = value;
            }
        }


        static FileMonitorHelper()
        {
            ThreadPool.QueueUserWorkItem(FileSpool, Path);
        }


        /// <summary>
        /// 定时轮巡本地配置文件，以便将最新配置加载到内存中
        /// </summary>
        /// <param name="para">配置文件所在路径</param>
        private static void FileSpool(object para)
        {
            while (true)
            {
                try
                {
                    string path = (string)para;

                    //读取当前文件修改时间，并存入哈希表
                    DirectoryInfo di = new DirectoryInfo(path);
                    Dictionary<string, FileInfo> fileHash = di.GetFiles("*.*", SearchOption.AllDirectories).ToDictionary(p => p.Name);

                    while (true)
                    {
                        try
                        {
                            //轮巡时间间隔
                            System.Threading.Thread.Sleep(UpdateInterval);

                            FileInfo[] files = di.GetFiles("*.*", SearchOption.AllDirectories);

                            bool changed = false;
                            foreach (FileInfo fi in files)
                            {
                                //配置文件被修改，重新加载配置
                                if (fileHash.ContainsKey(fi.Name)
                                    && fi.LastWriteTime != fileHash[fi.Name].LastWriteTime)
                                {
                                    if (OnFileModified != null)
                                    {
                                        OnFileModified(fi);
                                    }
                                    changed = true;
                                }
                                //如果fileHash中不包含这个文件，说明这个配置是新加进来的；
                                else if (!fileHash.ContainsKey(fi.Name))
                                {
                                    lock (_DicLock)
                                    {
                                        //重新加载<配置文件名，配置文件全路径>字典
                                        MonitorFileFullPathDic = null;
                                    }

                                    //OnFileModified(fi);
                                    changed = true;
                                }
                            }

                            //若有任何改动 重新获取Hash；
                            if (changed)
                            {
                                fileHash = files.ToDictionary(p => p.Name);
                                if (OnFileModifyCompleted != null)
                                {
                                    OnFileModifyCompleted();
                                }
                            }
                        }
                        catch (ThreadAbortException) { }
                        catch (Exception ex)
                        {
                            string msg = "轮询时发生异常（本地文件）";
                            EmailHelper.SendMail(_ErrorEmailFrom, _ErrorEmailTo, _ErrorEmailSubject, false, _HostName + "\n" +msg + "\n" + ex, "", "");
                        }
                    }
                }
                catch (ThreadAbortException) { }
                catch (Exception ex)
                {
                    string msg = "轮询时发生异常（本地文件），可能无法读取配置文件FileInfo,程序将重新启动轮询。Path = " + (string)para;
                    EmailHelper.SendMail(_ErrorEmailFrom, _ErrorEmailTo, _ErrorEmailSubject, false, _HostName + "\n" + msg + "\n" + ex, "", "");
                }
                Thread.Sleep(ran.Next(MIN_RETRY_MS, MAX_RETRY_MS));
            }
        }
    }
}
